package references

import "jvmgo/ch11/instructions/base"
import "jvmgo/ch11/rtda"
import "jvmgo/ch11/rtda/heap"

// 调用实例方法
// 依据实例类型进行分派
type INVOKE_VIRTUAL struct {
    base.Index16Instruction
}

func (self *INVOKE_VIRTUAL) Execute(frame *rtda.Frame) {
    currentClass := frame.Method().Class()
    cp := currentClass.ConstantPool()
    methodRef := cp.GetConstant(self.Index).(*heap.MethodRef)
    resolvedMethod := methodRef.ResolvedMethod()
    if resolvedMethod.IsStatic() {
        panic("java.lang.IncompatibleClassChangeError")
    }

    ref := frame.OperandStack().GetRefFromTop(resolvedMethod.ArgSlotCount() - 1)
    if ref == nil {
        panic("java.lang.NullPointerException")
    }

    if resolvedMethod.IsProtected() &&
        resolvedMethod.Class().IsSuperClassOf(currentClass) &&
        resolvedMethod.Class().GetPackageName() != currentClass.GetPackageName() &&
        ref.Class() != currentClass &&
        !ref.Class().IsSubClassOf(currentClass) {

        if !(ref.Class().IsArray() && resolvedMethod.Name() == "clone") {
            panic("java.lang.IllegalAccessError")
        }
    }

    methodToBeInvoked := heap.LookupMethodInClass(ref.Class(),
        methodRef.Name(), methodRef.Descriptor())
    if methodToBeInvoked == nil || methodToBeInvoked.IsAbstract() {
        panic("java.lang.AbstractMethodError")
    }

    base.InvokeMethod(frame, methodToBeInvoked)
}
